﻿using System;
using System.IO;
using System.Net;
using System.Text;

namespace Storage.Core
{
	internal class BinaryDeserializer : IBinaryDeserializer
	{
		private BinaryDeserializer(Stream inStream)
		{
			this.inStream = inStream;
		}

		public static BinaryDeserializer BeginRecord(Stream inStream)
		{
			if (!inStream.CanRead)
				throw new ArgumentException("Stream should be readable", "inStream");
			return new BinaryDeserializer(inStream);
		}

		public short ReadInt16()
		{
			return BitConverter.ToInt16(ReadFromStream(sizeof(Int16)), 0);
		}

		public ushort ReadUInt16()
		{
			return BitConverter.ToUInt16(ReadFromStream(sizeof(UInt16)), 0);
		}

		public Int32 ReadInt32()
		{
			return BitConverter.ToInt32(ReadFromStream(sizeof (Int32)), 0);
		}

		public UInt32 ReadUInt32()
		{
			return BitConverter.ToUInt32(ReadFromStream(sizeof (UInt32)), 0);
		}

		public Int64 ReadInt64()
		{
			return BitConverter.ToInt64(ReadFromStream(sizeof (Int64)), 0);
		}

		public UInt64 ReadUInt64()
		{
			return BitConverter.ToUInt64(ReadFromStream(sizeof (UInt64)), 0);
		}

		public float ReadFloat()
		{
			return BitConverter.ToSingle(ReadFromStream(sizeof (float)), 0);
		}

		public double ReadDouble()
		{
			return BitConverter.ToDouble(ReadFromStream(sizeof (double)), 0);
		}

		public bool ReadBool()
		{
			return ReadInt32() != 0;
		}

		public byte ReadByte()
		{
			return (byte) inStream.ReadByte();
		}

		public Guid ReadGuid()
		{
			return new Guid(ReadFromStream(16));
		}

		public byte[] ReadByteArray()
		{
			Int32 size = ReadInt32();
			var result = new byte[size];
			inStream.Read(result, 0, result.Length);
			return result;
		}

		public string ReadString()
		{
			return ReadString(Encoding.UTF8);
		}

		public string ReadString(Encoding encoding)
		{
			return encoding.GetString(ReadByteArray());
		}

		public IPAddress ReadIPAddress()
		{
			return new IPAddress(ReadByteArray());
		}

		public IPEndPoint ReadIPEndPoint()
		{
			return new IPEndPoint(ReadIPAddress(), ReadInt32());
		}

		public TimeSpan ReadTimeSpan()
		{
			return new TimeSpan(ReadInt64());
		}

		public DateTime ReadDateTime()
		{
			return DateTime.FromBinary(ReadInt64());
		}

		private byte[] ReadFromStream(int size)
		{
			ReadFromStream(simpleTypeBuffer, 0, size);
			return simpleTypeBuffer;
		}

		private void ReadFromStream(byte[] dest, int offset, int count)
		{
			inStream.Read(dest, offset, count);
		}

		private readonly Stream inStream;
		private readonly byte[] simpleTypeBuffer = new byte[16];
	}
}